#ifndef	REEDSOLOMON_H
#define	REEDSOLOMON_H
//
//	КОДИРОВКА РИДА-СОЛОМОНА
//	
//	предназначена для передачи данных по зашумлённым линиям. 
//	Заранее определяется максимально допустимое количество ошибок, 
//	которые могут возникнуть при передаче и сообщение кодируется 
//	таким образом, что если ошибок будет не больше этого 
//	количества, то на принимающей стороне данные удастся 
//	полностью восстановить. 
//	
//	Под ошибкой понимается неверный байт, неважно сколько битов 
//	в нём ошибочны. Неверный байт - это одна ошибка.
//	
//	БАЗОВЫЕ ФУНКЦИИ
//
//	Нужны нечасто, в сложных случаях, когда размер и формат 
//	передаваемого сообщения заранее неизвестны. Для простых случаев над 
//	ними есть "обёртки", см. раздел "синтаксический сахар" ниже.
//	
//	ReedSolomonEncoding::rsEncode – кодирование
//	ReedSolomonEncoding::rsDecode – декодирование
//	
//	Обращаться к ним нужно, явно указывая пространство имён, а 
//	именно ReedSolomonEncoding::rsDecode и ReedSolomonEncoding::rsEncode
//	
//	У обеих функций три параметра:
//	- адрес буфера (типа uint8_t *) в котором сначала находятся служебные
//		байты, а затем собственно данные для кодирования / раскодирования. 
//		Длина служебной области равна удвоенному количеству допустимых 
//		ошибок передачи.
//	- количество допустимых ошибок передачи
//	- длина передаваемого буфера (целиком и служебных данных, и реальных).
//	
//	ВОЗВРАЩАЕМЫЕ ЗНАЧЕНИЯ
//	
//	Фунция ReedSolomonEncoding::rsEncode возвращает код ошибки (из 
//	enum RS_RESULTS) или код RSE_NO_ERROR, если всё в порядке
//	
//	Функция ReedSolomonEncoding::rsDecode возвращает отрицательное 
//	значение (из enum RS_RESULTS) в случае ошибки. Если же всё в 
//	порядке, то она возвращает неотрицательное значение равное 
//	количеству исправленных ошибок передачи.
//	
//	На стороне передатчика следует закодировать сообщение и весь буфер 
//	передать. На стороне приёмника следует считать всё в буфер и раскодировать 
//	его. Полученные данные будут находиться не сначала буфера (сначала там 
//	служебные байты), а со смещением равным удвоенному количеству допустимых 
//	ошибок.
//	
//	См. приложенный пример №7, там всё это проделывается от начала до конца. 
//	Только вместо реальной передачи, просто вносятся случайные ошибки 
//	в уже закодированный буфер.
//	
//	СИНТАКСИЧЕСКИЙ САХАР
//	
//	Для упрощения жизни в простых случаях (когда длина и структура 
//	передаваемого сообщения известны заранее) добавлены макросы, 
//	позволяющие объявить передаваемый буфер правильно (оставив нужное 
//	количество служебных символов и т.п.).
//	
//	При использовании макросов передаваемое сообщение представляет 
//	собой одну единицу данных некоторого типа (встроенного и 
//	определённого пользователем). Тип может быть почти любым, но 
//	виртуальные методы в нём не допускаются. Если нужно передать 
//	массив фиксированной длины, то надо объявить соответствующий 
//	тип (см. приложенные примеры №№ 3 и 4. 
//	
//	Макрос DECLARE_PAYLOAD имеет три параметра
//	
//	- имя объявляемой переменной;
//	- тип данных передаваемой информации;
//	- количество допустимых ошибок передачи.
//	
//	Данный макрос не предполагает возможности проинициализировать 
//	создаваемую величину. Это нужно будет делать вручную. Доступ к 
//	объявляемой переменной возможен по заданному в макросе имени и 
//	имени поля payload (через точку). Поле payload имеет тип, 
//	указанный вторым параметром макроса.
//	
//	См. примеры №№ 2, 4 и 6, там объявлены пакеты для передачи 
//	числа, массива и сложной структуры без инициализации при 
//	объявлении.
//	
//	Макросы DECLARE_PAYLOAD_INIT и END_OF_DECLARE_PAYLOAD_INIT 
//	
//	сделаны специально для того, чтобы была возможность объявлять 
//	пакеты для передачи с инициализацией.
//	
//	Параметры DECLARE_PAYLOAD_INIT такие же, как у DECLARE_PAYLOAD
//	Макрос END_OF_DECLARE_PAYLOAD_INIT параметров не имеет.
//	
//	Сначала записывается вызов макроса DECLARE_PAYLOAD_INIT со 
//	всеми параметрами, затем пишется инициализация величины типа, 
//	который был передан макросу вторым параметров (если это простой 
//	тип вроде int, то число, если массив или структура то 
//	инциализатор в фигурных скобках). 
//	После данных для инициализации вызывается макрос 
//	END_OF_DECLARE_PAYLOAD_INIT
//	
//	Доступ к созданному пакету по-прежнему возможен по заданному в 
//	макросе DECLARE_PAYLOAD_INIT  имени и имени поля payload. 
//	Поле payload имеет тип, указанный вторым параметром макроса.
//	
//	См. примеры №№ 1, 3 и 5, там объявлены пакеты для передачи числа,
//	массива и сложной структуры с инициализацией непосредственно 
//	при объявлении.
//	
//	Объекты созданные макросами DECLARE_PAYLOAD и DECLARE_PAYLOAD_INIT
//	следует передавать (в качестве едиственного параметра) функциям 
//	rsEncode и rsDecode БЕЗ указания пространства имён. Эти функции -
//	просто обёртки для рассмотренных выше функций из пространства имён
//	ReedSolomonEncoding. Возвращаемые значения у них те же самые.
//	
//	Примеры №№ 1-6 иллюстрируют все необходимые действия.
//
//
// Внимание!!! ЗАПРОС ДОПОЛНИТЕЛЬНОЙ ПАМЯТИ
// ----------------------------------------
//	
//	Для работы функций кодирования декодирования нужна дополнительная память
//	которую они динамически запрашивают (а перед выходом освобождают).
//	Размер запрашиваемой зависит от максимаьно допустимого количества ошибок.
//	Если допусается Е ошибок, то будет запрашиваться дополнительная память
//	1) при кодировании: 7+4*Е байтов
//	2) при декодировании: 20+14*Е байтов
//	Если память выделить не удалось, функции вернут RSE_NOT_ENOUGH_MEMORY
//	Это лучше бы всегда проверять.
//

enum RS_RESULTS {
	RSE_NOT_ENOUGH_MEMORY = -5,
	RSE_BUFFER_TOO_SMALL = -4,
	RSE_LARGE_AMOUNT_OF_ERRORS = -3,
	RSE_NO_ERROR = -2,
	RSE_TOO_MANY_ERRORS = -1
};

template <typename T, size_t amountOfErrors>
union __attribute__ ((packed)) __CRSPayload {
	uint8_t __pl__[sizeof(T) + amountOfErrors * 2];
	struct __attribute__ ((packed)) {
		uint8_t __p2__[amountOfErrors * 2];
		T payload;
	};
};

#define DECLARE_PAYLOAD(name, type, errors) __CRSPayload<type, errors> name;
#define DECLARE_PAYLOAD_INIT(name, type, errors) __CRSPayload<type, errors> name = { payload: 
#define END_OF_DECLARE_PAYLOAD_INIT };

namespace ReedSolomonEncoding {
	extern RS_RESULTS rsEncode(void *buf, const uint8_t amountOfErrors, const uint8_t bufsize);
	extern int rsDecode(void *buf, const uint8_t amountOfErrors, const uint8_t bufsize);
}

template <class PL>
RS_RESULTS rsEncode(PL & pl) {
	return ReedSolomonEncoding::rsEncode(& pl, (sizeof(pl) - sizeof(pl.payload)) / 2, sizeof(pl));
}

template <class PL>
int rsDecode(PL & pl) {
	return ReedSolomonEncoding::rsDecode(& pl, (sizeof(pl) - sizeof(pl.payload)) / 2, sizeof(pl));
}

#endif	//	REEDSOLOMON_H
